package cn.stimd.spring.beans.factory.support;

import cn.stimd.spring.beans.BeanUtils;
import cn.stimd.spring.beans.factory.DisposableBean;
import cn.stimd.spring.beans.factory.config.BeanPostProcessor;
import cn.stimd.spring.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 待销毁对象的适配器类，负责实际的销毁工作
 * <p>
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class DisposableBeanAdapter implements DisposableBean {
    private static final String CLOSE_METHOD_NAME = "close";
    private static final String SHUTDOWN_METHOD_NAME = "shutdown";
    private static Class<?> closeableInterface = AutoCloseable.class;

    private final Object bean;      //待销毁的实例
    private final String beanName;
    private String destroyMethodName;
    private transient Method destroyMethod;
    private List<DestructionAwareBeanPostProcessor> beanPostProcessors;

    public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition beanDefinition, List<BeanPostProcessor> postProcessors) {
        this.bean = bean;
        this.beanName = beanName;
        //推断销毁方法的名称
        String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
        if(destroyMethodName != null){
            this.destroyMethodName = destroyMethodName;
            this.destroyMethod = findDestroyMethod();
        }
        this.beanPostProcessors = filterPostProcessors(postProcessors, bean);
    }

    @Override
    public void destroy() throws Exception {
        //执行@PreDestroy注解的方法
        if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
            for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
                processor.postProcessBeforeDestruction(this.bean, this.beanName);
            }
        }

        //调用DisposableBean接口
        if(this.bean instanceof DisposableBean){
            ((DisposableBean) bean).destroy();
        }

        //调用自定义或默认的销毁方法
        if (this.destroyMethod != null) {
            ReflectionUtils.makeAccessible(this.destroyMethod);
            destroyMethod.invoke(this.bean, (Object[]) null);
        }
    }


    //如果需要销毁，则将对应的BeanPostProcessor添加到列表中
    private List<DestructionAwareBeanPostProcessor> filterPostProcessors(List<BeanPostProcessor> processors, Object bean) {
        List<DestructionAwareBeanPostProcessor> filteredPostProcessors = null;
        if(!CollectionUtils.isEmpty(processors)){
            filteredPostProcessors = new ArrayList<>();
            for (BeanPostProcessor processor : processors) {
                if(processor instanceof DestructionAwareBeanPostProcessor){
                    DestructionAwareBeanPostProcessor dabpp = (DestructionAwareBeanPostProcessor) processor;
                    //判断Bean是否需要销毁，比如声明@PreDestroy注解
                    if(dabpp.requiresDestruction(bean)){
                        filteredPostProcessors.add(dabpp);
                    }
                }
            }
        }
        return filteredPostProcessors;
    }


    //推断销毁方法的名称
    //要么是BeanDefinition指定的销毁方法，要么是AutoCloseable接口的默认方法
    private String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
        //case-1: 指定的销毁方法
        String destroyMethodName = beanDefinition.getDestroyMethodName();
        //case-2: 默认的销毁方法
        //1) 不能指定自定义销毁方法
        //2) 必须是AutoCloseable接口的实现类
        if(destroyMethodName == null && closeableInterface.isInstance(bean)){
            //不能是DisposableBean接口的实现类
            if(!(bean instanceof DisposableBean)){
                try {
                    //3) 方法名是close或shutdown
                    return bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
                } catch (NoSuchMethodException e) {
                    try {
                        return bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
                    } catch (NoSuchMethodException ex) {
                        //ignore
                    }
                }
            }
            return null;
        }
        return destroyMethodName;
    }


    private Method findDestroyMethod() {
        return BeanUtils.findMethod(this.bean.getClass(), this.destroyMethodName);
    }


    public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
        if(bean instanceof DisposableBean || closeableInterface.isInstance(bean)){
            return true;
        }

        String destroyMethodName = beanDefinition.getDestroyMethodName();
        return StringUtils.hasLength(destroyMethodName);
    }


    public static boolean hasApplicableProcessors(Object bean, List<BeanPostProcessor> postProcessors) {
        for (BeanPostProcessor processor : postProcessors) {
            if (processor instanceof DestructionAwareBeanPostProcessor) {
                DestructionAwareBeanPostProcessor dabpp = (DestructionAwareBeanPostProcessor) processor;
                if (dabpp.requiresDestruction(bean)) {
                    return true;
                }
            }
        }
        return false;
    }
}
